#include <iostream>
#include <string>
#include <sys/wait.h>
#include <sys/types.h>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <string.h>
using namespace std;

const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;

//退出码
int lastcode = 0;

//全局的命令行参数
char* gargv[argvnum];
int gargc = 0;

// 全局的shell的工作路径
char pwd[basesize];
char pwdenv[basesize];

//我的系统的环境变量
char *genv[envnum];

string GetUserName()
{
  string name = getenv("USER");
  return name.empty() ? "None" : name;
}

string GetHostName()
{
  string hostname = getenv("HOSTNAME");
  return hostname.empty() ? "None" : hostname;
}

string GetPwd()
{
  //string pwd = getenv("PWD");
  if(nullptr == getcwd(pwd,sizeof(pwd))) return "None";
  snprintf(pwdenv,sizeof(pwdenv), "PWD=%s",pwd);
  putenv(pwdenv);
  return pwd;
  //return pwd.empty() ? "None" : pwd;
}

string LastDir()
{
  string curr = GetPwd();
  if(curr == "/" || curr == "None") return curr;
  size_t pos = curr.rfind("/");
  if(pos == std::string::npos) return curr;
  return curr.substr(pos+1);
}

string MakeCommandLine()
{
  //[jk@hcss-ecs-3e3f MYShell]$
  char command_line[basesize];
  snprintf(command_line,basesize,"[%s@%s %s]# ", GetUserName().c_str(),GetHostName().c_str(),LastDir().c_str());
  return command_line;
}

void  PrintCommandLine() //打印命令
{
  printf("%s",MakeCommandLine().c_str());
  fflush(stdout);
}

bool GetCommandLine(char* command_buffer,int size) //获取用户命令
{
  //将用户输入的命令作为完整的字符串"ls -a -l"
  char *result = fgets(command_buffer,size,stdin);
  if(!result)
  {
    return false;
  }
  command_buffer[strlen(command_buffer) - 1] = 0;//去掉\n,如果什么命令都没有，至少会有一个回车键。
  if(strlen(command_buffer) == 0) return false;

  return true;
}

bool ParseCommandLine(char*command_buffer,int len) //分析命令
{
  memset(gargv,0,sizeof(gargv));
  gargc = 0;
  const char* sep = " ";
  gargv[gargc++] = strtok(command_buffer,sep);
  while((bool)(gargv[gargc++] = strtok(nullptr,sep)));
  gargc--;

  return true;
}
void debug()
{
  printf("argc:%d\n",gargc);
  for(int i = 0; gargv[i]; i++)
  {
    printf("argv[%d]:%s\n",i,gargv[i]);
  }
}
//在shell中
//有些命令，必须由子进程执行
//有些命令，要由shell自己执行，这样的命令是内建命令
bool ExecuteCommandLine() //执行命令
{
  //让子进程执行命令
  pid_t id = fork();
  if(id < 0) return false;
  if(id == 0)
  {
    //子进程
    //执行命令 exec
    execvpe(gargv[0],gargv,genv);
    //退出
    exit(1);

  }
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    if(WIFEXITED(status))
    {
      lastcode = WEXITSTATUS(status);
    }
    else{
      lastcode = 100;
    }
    return true;
  }
  return false;
}

void AddEnv(const char* item)
{
   int index = 0;
   while(genv[index])
   {
     index++;
   }
   genv[index] = (char*)malloc(strlen(item)+1);
   strncpy(genv[index],item,strlen(item)+1);
   genv[++index] = nullptr;
}

bool CheckAndExecBuiltCommand()
{ 
  if(strcmp(gargv[0],"cd") == 0)
  {
    //内建命令
    if(gargc == 2)
    {
       chdir(gargv[1]);
    }
    else
    {
      lastcode = 1;
    }
      return true;
  }
  else if(strcmp(gargv[0],"export") == 0 )
  {
    //export
    if(gargc == 2)
    {
      AddEnv(gargv[1]);
    }
    else{
      lastcode = 2;
    }
    return true;
  }
  else if(strcmp(gargv[0], "env") == 0)
  {
    for(int i = 0;genv[i];i++)
    {
      printf("%s\n",genv[i]);
    }
    lastcode = 0;
    return true;
  }
  else if(strcmp(gargv[0],"echo") == 0)
  {
    if(gargc == 2)
    {
      //echo $?   //查本地变量
      //echo $PATH  //查环境变量
      //echo hello  
      if(gargv[1][0] == '$')
      {
        if(gargv[1][1] == '?')
        {
          printf("%d\n",lastcode);
          lastcode = 0;
        }
      }
      else{
        printf("%s\n",gargv[1]);
        lastcode = 0;
      }
    }
    else{
      lastcode = 3;
    }
    return true;
  }
  return false;
}

void InitEnv()
{
  extern char **environ;
  int index = 0;   
  while(environ[index])
  {
    genv[index] =(char*) malloc(strlen(environ[index]) +1 );
    strncpy(genv[index],environ[index],strlen(environ[index])+1);
    index++;
  }

  genv[index] = nullptr;
}

int main()
{
  InitEnv();
  char command_buffer[basesize];
  while(true)
  {
     PrintCommandLine(); //打印命令
     if( !GetCommandLine(command_buffer,basesize))
     {
      continue;
     }//获取用户命令
      //printf("%s", command_buffer); //提供的字符串最好是纯净的，由于每次输入我们都会按回车"\n"，将输入的命令去掉最后的"\n"
      
     //要调用命令，最终都要使用到exec类似的接口，都需要将将"ls -a -l -s"->"ls" "-a" "-l" "-s"
      ParseCommandLine(command_buffer,strlen(command_buffer)); //分析命令
    //debug();  
    
      //检测内建命令
        if(CheckAndExecBuiltCommand())
        {
          continue;
        }
      ExecuteCommandLine(); //执行命令
  
  }
  return 0;
}
